﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using Javavaj.iHunter;
using Javavaj.iHunter.Core;
using Javavaj.iHunter.Database;
using Javavaj.iHunter.Viewers;

namespace Javavaj.iHunter.Core
{
    public partial class PluginManager : Component
    {
        Dictionary<string, IViewer> viewers;
        List<IFilter> filters;
        List<IChannelFactory> channelFactories;
        List<PluginInformation> infos;
        Dictionary<PluginInformation, IPlugin> pluginDict;
        IViewer defaultViewer = new DefaultViewer();
        List<string> tempFiles;

        public IViewer GetViewer(string docType)
        {
            if (viewers.ContainsKey(docType))
                return viewers[docType];
            
            return defaultViewer;
        }
        public PluginManager()
        {
            tempFiles = new List<string>();
            InitializeComponent();
        }

        public PluginManager(IContainer container)
        {
            container.Add(this);
            tempFiles = new List<string>();

            InitializeComponent();
        }

        private UserDbDataSet dataSet;

        [DefaultValue(null)]
        public UserDbDataSet DataSet
        {
            get
            {
                return dataSet;
            }
            set
            {
                dataSet = value;

            }
        }


        public void LoadPlugins()
        {
            //Load Plugins
            pluginDict = new Dictionary<PluginInformation, IPlugin>();
            infos = new List<PluginInformation>();

            channelFactories = new List<IChannelFactory>();
            viewers = new Dictionary<string,IViewer>();
            filters = new List<IFilter>();


            string assemblySearchPath = Path.Combine(
               Path.Combine(
                   Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                   "iHunter"
                   ),
                 "Plugins"
                 );

            if (!Directory.Exists(assemblySearchPath))
            {
                Directory.CreateDirectory(assemblySearchPath);
            }

            foreach (string path in Directory.GetFiles(assemblySearchPath, "*.dll", SearchOption.TopDirectoryOnly))
            {
                LoadPluginFromAssembly(path, false);
            }
            
            assemblySearchPath = Path.Combine(
                    Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName),
                   "Plugins"
                   );

            if (Directory.Exists(assemblySearchPath))
            {
                foreach (string path in Directory.GetFiles(assemblySearchPath, "*.dll", SearchOption.TopDirectoryOnly))
                {
                    LoadPluginFromAssembly(path, true);
                }
            }
        }

        public void SetPluginInfo(PluginInformation plugin)
        {
            //查找配置信息
            var result = from row in dataSet.PluginSettings
                         where row.AssemblyName == plugin.Assembly
                         select row;

            var rows = result.ToArray();
            UserDbDataSet.PluginSettingsRow settingRow = null;

            if (rows.Length == 0)
            {
                return;
            }
            else
            {
                settingRow = rows[0];
            }

            settingRow.Enabled = plugin.Enabled;
            settingRow.AcceptChanges();
            return;

        }

        public void LoadPluginFromAssembly(string path, bool glbl)
        {
            string assemblyPath = null;
            if (!Path.IsPathRooted(path))
            {
                if (glbl)
                {
                    string assemblySearchPath = Path.Combine(
                        Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName),
                        "Plugins"
                        );
                    assemblyPath = Path.Combine(assemblySearchPath, path);


                }
                else
                {
                    string assemblySearchPath = Path.Combine(
                        Path.Combine(
                            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                            "iHunter"
                            ),
                        "Plugins"
                        );
                    assemblyPath = Path.Combine(assemblySearchPath, path);

                }
            }
            else
            {
                assemblyPath = path;
            }
        
            Assembly assembly = null;
            try
            {
                byte[] bytes = File.ReadAllBytes(path);
                assembly = System.Reflection.Assembly.Load(bytes);
            }
            catch
            {
                return;
            }

            PluginInformation info = new PluginInformation() { Assembly = assembly.FullName };
            if (pluginDict.ContainsKey(info))
                return;

            try
            {
                foreach (Module module in assembly.GetModules())
                {
                    foreach (Type type in module.GetTypes())
                    {
                        //仅查找公共类
                        if (type.IsNotPublic || !type.IsClass)
                            continue;
                        //不尝试实例化泛型类
                        if (type.IsGenericType)
                            continue;

                        //不尝试实例化抽象类
                        if (type.IsAbstract)
                            continue;

                        foreach (Type iface in type.GetInterfaces())
                        {

                            if (!(iface == typeof(IChannelFactory) ||
                            iface == typeof(IViewer) ||
                            iface == typeof(IFilter)
                                ))
                                continue;



                            //查找配置信息
                            var result = from row in dataSet.PluginSettings
                                         where row.AssemblyName == assembly.FullName
                                         select row;

                            var rows = result.ToArray();
                            UserDbDataSet.PluginSettingsRow settingRow = null;

                            if (rows.Length == 0)
                            {
                                settingRow = dataSet.PluginSettings.AddPluginSettingsRow(assembly.FullName, true, "");
                                dataSet.PluginSettings.AcceptChanges();
                            }
                            else
                            {
                                settingRow = rows[0];
                            }


                            XmlDocument settings = new XmlDocument();
                            try
                            {
                                string settingXmlText = settingRow.Settings;
                                settings.LoadXml(settingXmlText);
                            }
                            catch
                            {
                            }

                            //在类实现的所有接口中寻找插件接口: IChannelFactory, IViewer, IFilter
                            PluginWrapper p = null;
                            PluginType pluginType = PluginType.Unknown;

                            info.Assembly = assembly.FullName;
                            info.Global = glbl;
                            info.Name = assembly.GetName().Name;
                            info.Enabled = settingRow.Enabled;
                            info.FullPath = assemblyPath;
                            info.Version = assembly.GetName().Version.ToString(4);

                            if (settingRow.Enabled)
                            {

                                if (iface == typeof(IChannelFactory))
                                {
                                    IChannelFactory plugin = CreateChannelFactory(type);
                                    p = new ChannelFactoryWrapper(plugin);
                                    p.PluginError += delegate(object sender, PluginErrorEventArgs args)
                                    {
                                        OnPluginError(info, args.Exception);
                                    };
                                    channelFactories.Add(p as IChannelFactory);
                                    pluginType = PluginType.ChannelPlugin;
                                }
                                if (iface == typeof(IViewer))
                                {
                                    IViewer plugin = CreateViewer(type);
                                    p = new ViewerWrapper(plugin);
                                    p.PluginError += delegate(object sender, PluginErrorEventArgs args)
                                    {
                                        OnPluginError(info, args.Exception);
                                    };
                                    foreach ( string docType in plugin.DocumentTypes)
                                        viewers.Add(docType, p as IViewer);
                                    pluginType = PluginType.ViewerPlugin;
                                }

                                if (iface == typeof(IFilter))
                                {
                                    IFilter plugin = CreateFilter(type);
                                    p = new FilterWrapper(plugin);
                                    p.PluginError += delegate(object sender, PluginErrorEventArgs args)
                                    {
                                        OnPluginError(info, args.Exception);
                                    };
                                    filters.Add(p as IFilter);
                                    pluginType = PluginType.FilterPlugin;
                                }
                            }
                            info.PluginType = pluginType;

                            infos.Add(info);
                            if (settingRow.Enabled && p != null)
                            {

                                pluginDict.Add(info, p);
                                p.SettingChanged += delegate(object sender, PluginSettingChangedEventArgs e)
                                {
                                    OnPluginSettingChanged(this,p.Plugin, e.Settings);
                                };
                                p.InitSettings(settings);

                            }
                        }



                    }
                }
            }
            catch(Exception e)
            {
                OnPluginError(info, e);
            }
    
               
        }

        private void OnPluginSettingChanged(object sender, IPlugin plugin, XmlDocument settings)
        {
            if (PluginSettingChanged != null)
                PluginSettingChanged.Invoke(this, new PluginSettingChangedEventArgs(plugin, settings));
        }

        public PluginInformation[] GetPluginInformations(bool listDiabled)
        {
            var result = from info in infos
                         where listDiabled || info.Enabled
                         select info;
            return infos.ToArray();
        }

        public IPlugin GetPlugin(PluginInformation info)
        {
            IPlugin plugin;
            if (pluginDict.TryGetValue(info, out plugin))
                return plugin;
            else
                return null;
        }
        public IEnumerable<IPlugin> ListPlugins(PluginType type)
        {
            switch (type)
            {
                case PluginType.ChannelPlugin:

                    foreach (var plugin in channelFactories)
                        yield return plugin;
                    yield break;


                case PluginType.FilterPlugin:

                    foreach (var plugin in filters)
                        yield return plugin;
                    yield break;


                case PluginType.ViewerPlugin:

                    foreach (var plugin in viewers.Values)
                        yield return plugin;

                    yield break;

                default:
                    yield break;
            }


        }



        private IFilter CreateFilter(Type type)
        {
                System.Reflection.ConstructorInfo ctor = type.GetConstructor(System.Type.EmptyTypes);
                IFilter filter = ctor.Invoke(new object[] { }) as IFilter;
                return filter;
        }
        private IViewer CreateViewer(Type type)
        {
            System.Reflection.ConstructorInfo ctor = type.GetConstructor(System.Type.EmptyTypes);
            IViewer viewer = ctor.Invoke(new object[] { }) as IViewer;
            return viewer;
        }

        private IChannelFactory CreateChannelFactory(Type type)
        {
            System.Reflection.ConstructorInfo ctor = type.GetConstructor(System.Type.EmptyTypes);
            IChannelFactory factory = ctor.Invoke(new object[] { }) as IChannelFactory;
            return factory;
        }


        public event PluginSettingChangedEventHandler PluginSettingChanged;
        public void Cleanup()
        {

            foreach (string filename in tempFiles)
            {
                try
                {
                    System.IO.File.Delete(filename);
                }
                catch
                {
                }
            }
            tempFiles.Clear();

        }
        protected void OnPluginError(PluginInformation info, Exception e)
        {
            if (PluginError != null)
                PluginError.Invoke(this, new PluginErrorEventArgs(info, e));
        }
        public event PluginErrorEventHandler PluginError;
    }

    public class PluginErrorEventArgs : EventArgs
    {
        PluginInformation plugin;

        public PluginInformation Plugin
        {
            get { return plugin; }
        }
        Exception exception;

        public Exception Exception
        {
            get { return exception; }
        }
        public PluginErrorEventArgs(PluginInformation plugin, Exception excpetion)
            : base()
        {
            this.plugin = plugin;
            this.exception = exception;
        }
    }

    public delegate void PluginErrorEventHandler(object sender, PluginErrorEventArgs e);
}
